package cn.csq.rpc.client;

import cn.csq.rpc.Request;
import cn.csq.rpc.Response;
import cn.csq.rpc.ServiceDescriptor;
import cn.csq.rpc.code.Decoder;
import cn.csq.rpc.code.Encoder;
import cn.csq.rpc.transport.HTTPTransportClient;
import cn.csq.rpc.transport.TransportClient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author csq
 * @version 1.0.0
 * @ClassName RemoteInvoke r.java
 * @Description 调用远程服务的代理类
 * @createTime 2021年12月20日 23:22:00
 */
@Data
@Slf4j
public class RemoteInvoker implements InvocationHandler {


    private Class clazz;
    private Encoder encoder;
    private Decoder decoder;
    private TransportSelector transportSelector;

    public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder, TransportSelector selector) {
        this.clazz = clazz;
        this.decoder = decoder;
        this.encoder = encoder;
        this.transportSelector = selector;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //整个rpc的核心就在于远程的代理调用
        //构造一个请求，通过网络将这个请求发出去 发送给server
        //请求server的响应
        //然后拿到返回的数据
        Request request = new Request();
        request.setService(ServiceDescriptor.from(clazz, method));
        request.setParameters(args);

        //发送请求
        Response response = invokeRemote(request);
        if (response == null || response.getCode() != 0) {
            throw new IllegalStateException("fail to invoke remote:" + response.getMessage());
        }
        return response.getData();
    }

    private Response invokeRemote(Request request) {
        //获取网络连接client
        TransportClient client = null;
        Response response = null;
        try {
            client = transportSelector.select();

            byte[] outBytes = encoder.encode(request);
            //获取数据
            InputStream revice = client.write(new ByteArrayInputStream(outBytes));
            byte[] intBytes = IOUtils.readFully(revice, revice.available());

            response = decoder.decode(intBytes, Response.class);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            response = new Response();
            response.setCode(1);
            response.setMessage("RpcClient got error:" + e.getClass() + " " + e.getMessage());
        } finally {
            if (client != null) {
                transportSelector.release(client);
            }
        }
        return response;
    }

}
